
/* Generated by Interface Builder */

#import "DictManager.h"
#import <strings.h>
#import <stdlib.h>
#import <appkit/Panel.h>
#import <appkit/NXBrowser.h>
#import <appkit/NXBrowserCell.h>
#import <appkit/Text.h>
#import <objc/HashTable.h>

#define DICTKNT 3  /* number of dictionaries implemented */
const char *dictType[DICTKNT] = {"@","i","*"} ;
id dict[DICTKNT] ;

#define CHUNK 127
char **addString(const char *string, int length, char **list,
        int count)
// Adds the specified string to a list of strings.  It allocates 
// more memory in chunks as needed. Robbed and hacked from
// the BusyBox app.
{ if(!list)
    list = (char **)malloc(CHUNK*sizeof(char *));
  list[count] = (char *)malloc((length+1)*sizeof(char));
  strcpy(list[count], string);
  count++;
  if(!(count% CHUNK))
    list = (char **)realloc(list,(((count/CHUNK)+1)*CHUNK)*sizeof(char *));
  list[count] = NULL;
  return list;
}

void freeList(char **list)
// Frees the array of strings
{ char **strings;
  if(list)
  { strings = list;
    while (*strings)
      free(*strings++);
    free(list);
  }
}

char *
copy(aStr)
char *aStr ;
{ // mallocs memory for a copy of aStr, returns pointer
  // to copy
  char *newStr ;
  newStr = (char *) malloc(strlen(aStr) + 1) ;
  strcpy(newStr, aStr) ;
  return newStr ;
}


@implementation DictManager

+ initialize ;
{ // set up the global dictionaries
  int i ;
  for(i = 0 ; i < DICTKNT ; i++)
  { dict[i] = [HashTable new] ;
    [dict[i] initKeyDesc: "*" valueDesc: dictType[i]] ;
  }
  return self ;
}


+ insertKey: (char *) aKey value: (void *) data type: (char *) aType ;
{ // insert aKey<->data pair into the dictionary indicated by aType;
  int i ;
  if(!strcmp(aType,"*")) // handle strings as special case
  { if([dict[2] isKey: aKey]) // free up old data
       free([dict[2] insertKey:(const void *) copy(aKey) value:(void *) copy(data)]) ;
    else
      [dict[2] insertKey: (const void *) copy(aKey) value:(void *) copy(data)] ;
  }
  else
  { for(i = 0 ; i < DICTKNT && strcmp(aType,dictType[i]) ; i++) ;
    if([dict[i] isKey: aKey]) // already exists...
      [dict[i] insertKey: (const void *) copy(aKey)  value: (void *) data] ;
    else 
      [dict[i] insertKey: (const void *) copy(aKey)  value: (void *) data] ;
  }
  return self ;
}
      

+ removeKey: (char *) aKey type: (char *) aType ;
{ // insert aKey<->data pair into the dictionary indicated by aType;
  int i ;
  if(!strcmp(aType,"*")) // handle strings as special case
  { if([dict[2] isKey: aKey]) // free up old data
    { free([dict[2] valueForKey: aKey]) ;
      [dict[2] removeKey: aKey] ;
    }
  }
  else
  { for(i = 0 ; i < DICTKNT && strcmp(aType,dictType[i]) ; i++) ;
      [dict[i] removeKey: (const void *) aKey] ;
  }
  return self ;
}
      

+ (void *) valueForKey: (char *) aKey type: (char *) aType ;
{ int i ;
  for(i = 0 ; i < DICTKNT && strcmp(aType,dictType[i]) ; i++) ;
  if(i == DICTKNT)
  { NXRunAlertPanel("CB","Unknown data type: \"%s\"",NULL,NULL,NULL,aType) ;
    return (void *) 0 ;
  }
  else
    return [dict[i] valueForKey: aKey] ;
}

- (int)browser:sender fillMatrix:matrix inColumn:(int)column ;
{ if(column == 0)
    return DICTKNT ;
  else
  { char typeBuf[24] ;
    int i,j ;
    const void  *key; 
    void  *value; 
    NXHashState  state ;
    id table ;
    char **aList = NULL ;  
    strcpy(typeBuf, 
      [[[browserView matrixInColumn: 0] selectedCell] stringValue]) ;
    for(i = 0 ; i < DICTKNT && strcmp(typeBuf,dictType[i]) ; i++) ;
    table = dict[i] ; 
    state =  [table initState]; 
    j = 0 ;
    while ([table nextState: &state key: &key value: &value]) 
      aList = addString((const char *) key, strlen(key), aList, j++) ;
    freeList(browserList) ;
    browserList = aList ;
    return j ;
  }
}


- browser:sender loadCell:cell atRow:(int)row inColumn:(int)column ;
{ if(column == 0)
  { [cell setStringValue: dictType[row]] ;
    [cell setLeaf: NO] ;
  }
  else
  { [cell setStringValue: browserList[row]] ;
    [cell setLeaf:YES] ;
  }
  return self ;
}

- browserHit:sender ;
{ char path[128] ;
  char type[2], *key[128] ;
  if([browserView selectedColumn] != 1)
    return self ;
  [browserView setPathSeparator: ' '] ;
  [browserView getPath: path toColumn: 2] ;
  sscanf(path,"%s %s",&type,&key) ;
  if(!strcmp(type,"@"))
  { id anId = (id) [dict[0] valueForKey:key] ; 
    [textView setText: [anId name]] ;
    return self ;
  }
  if(!strcmp(type,"i"))
  { char buf[128] ;
    int i = (int) [dict[1] valueForKey:key] ;
    sprintf(buf,"%d", i) ;
    [textView setText: buf] ;
    return self ;
  }
  if(!strcmp(type,"*"))
  { [textView setText: [dict[2] valueForKey:key]] ;
    return self ;
  }  
  return self ;
}

- delete: sender ;
{ char path[128] ;
  char type[2], *key[128] ;
  int i = 0;
  // delete the selected dictionary entry
  if([browserView selectedColumn] != 1)
    return self ;
  [browserView setPathSeparator: ' '] ;
  [browserView getPath: path toColumn: 2] ;
  sscanf(path,"%s %s",&type,&key) ;
  if(!strcmp(type,"@"))
     i = 0 ;
  else if(!strcmp(type,"i"))
    i = 1 ;
  else if(!strcmp(type,"*"))
    i = 2 ;
  [dict[i] removeKey:key] ;
  sprintf(path," %s",&type) ;
  [browserView loadColumnZero] ;
  [browserView setPath: path] ;
  [textView setText: ""] ;
  return self ;
}

- makeKeyAndOrderFront: sender ;
{ [browserView loadColumnZero] ;
  return [super makeKeyAndOrderFront: sender] ;
}

- newValue: sender ;
{ char path[128] ;
  char type[2], key[128], *theText ;
  int textLen ;
  if([browserView selectedColumn] != 1)
    return self ;
  [browserView setPathSeparator: ' '] ;
  [browserView getPath: path toColumn: 2] ;
  sscanf(path,"%s %s",&type,&key) ;
  if(!strcmp(type,"@"))
  { [textView setText: "Sorry: cannot change value of an id"] ;
    return self ;
  }
  textLen = [textView textLength] ;
  theText = (char *) malloc(textLen + 1) ;
  [textView getSubstring: theText start:0 length: textLen] ; 
  if(!strcmp(type,"i"))
  { int i ;
    sscanf(theText,"%d",&i) ;
    [DictManager insertKey: key value: (void *) i type: "i"] ;
  }
  else if(!strcmp(type,"*"))
    [DictManager insertKey: key value: (void *) theText type: "*"] ;
  free(theText) ;
  return self ;
}  
@end
